package com.linsen.core.web.handler;

import cn.hutool.core.util.StrUtil;
import com.linsen.core.common.constant.LinsenConstant;
import com.linsen.core.common.context.TraceContextHolder;
import com.linsen.core.common.exception.BaseException;
import com.linsen.core.common.exception.LimitException;
import com.linsen.core.common.exception.RequestBusinessException;
import com.linsen.core.common.util.Result;
import com.linsen.core.web.props.LinsenWebProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingRequestHeaderException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

/**
 * @author zhaoyong
 * @date 2023/02/20
 */
@Slf4j
@RestControllerAdvice
@RequiredArgsConstructor
public class RequestExceptionHandler {
    private final LinsenWebProperties webProperties;

    @ExceptionHandler(RequestBusinessException.class)
    public Result handleRequestBusinessException(RequestBusinessException e) {
        return Result.error(e.getMessage(), e.getData());
    }

    @ExceptionHandler(BaseException.class)
    public Result handleBaseException(BaseException e) {
        if(e.getStatus() == HttpStatus.UNAUTHORIZED.value()){
            return Result.get(401, e.getMessage());
        }
        return Result.error(e.getMessage());
    }

    @ExceptionHandler(LimitException.class)
    public Result handleLimitException(LimitException e) {
        log.debug("limit meta:{}", e.getMeta());
        return Result.error(e.getMessage(), e.getExpire());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        return Result.get(400, bindingResult.hasErrors() ? bindingResult.getFieldErrors().get(0).getField() + StrUtil.COLON + bindingResult.getFieldErrors().get(0).getDefaultMessage() : null);
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    public Result handleNoHandlerFoundException(NoHandlerFoundException e) {
        return Result.get(404, e.getMessage());
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        return Result.get(405, e.getMessage());
    }

    @ExceptionHandler(MissingRequestHeaderException.class)
    public Result handleMissingRequestHeaderException(MissingRequestHeaderException e) {
        return Result.get(400, e.getMessage());
    }

    @ExceptionHandler(MissingPathVariableException.class)
    public Result handleMissingPathVariableException(MissingPathVariableException e) {
        return Result.get(400, e.getMessage());
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Result handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        return Result.get(400, e.getMessage());
    }

    @ExceptionHandler(Exception.class)
    public Result handleException(Exception e) {
        if (this.webProperties.getPrintStackTrace()) {
            log.error("", e);
        } else {
            log.error("\n" + "\n========== Exception =========" + "\nTrace Id: {}" + "\nException ClassName: {}" + "\nException Message: {}" + "\n========== Exception =========" + "\n", TraceContextHolder.getTraceId(), e.getClass(), e.getMessage());
        }

        return Result.error(LinsenConstant.DEFAULT_FAIL_MESSAGE);
    }
}
